package com.network.retrofit_rx.Api;

import android.accounts.NetworkErrorException;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.alibaba.fastjson.JSON;
import com.network.BuildConfig;
import com.network.utils.NetworkUtils;
import com.network.utils.RxUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

/**
 * 必须在application中调用{@see #build()},以创建{@see #httpService}
 *
 * @author QiQi
 * @date 2017/8/21
 */

public class HttpManager {
    private RetrofitApiInterface httpService;
    private List<Converter.Factory> factories;
    private List<Interceptor> interceptors;
    private String baseUrl;

    //add shibo.zheng start
    //提交json数据
    private static final MediaType MEDIATYPEJSON = MediaType.parse("application/json;charset=utf-8");
    //add shibo.zheng end

    public static HttpManager getInstance() {
        return HttpManager.SingleHolder.instance;
    }

    public HttpManager baseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
        return this;
    }

    public String getBaseUrl() {
        return baseUrl == null ? "" : baseUrl;
    }

    public void build(String token) {
        OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();
        if (interceptors != null) {
            okHttpBuilder.interceptors().addAll(interceptors);
        }
        if (BuildConfig.DEBUG) {
            HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(message -> {
                //打印retrofit日志
                Log.i("RetrofitLog", message);
            });
            loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            okHttpBuilder.addInterceptor(loggingInterceptor);
        }
        okHttpBuilder.addNetworkInterceptor(new TokenHeadInterceptor("Bearer " + token));
        okHttpBuilder.connectTimeout(5000, TimeUnit.MILLISECONDS);

        Retrofit.Builder builder = new Retrofit.Builder()
                .client(okHttpBuilder.build())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(baseUrl);
        if (factories != null) {
            for (Converter.Factory factory : factories) {
                builder.addConverterFactory(factory);
            }
        } else {
            builder.addConverterFactory(MyResponseBodyConverterFactory.create());
            builder.addConverterFactory(BooleanConverterFactory.create());
        }

        httpService = builder.build().create(RetrofitApiInterface.class);
    }

    public HttpManager addFactory(Converter.Factory factory) {
        if (factories == null) {
            factories = new ArrayList<>();
        }
        factories.add(factory);
        return this;
    }

    public HttpManager addInterceptor(Interceptor interceptor) {
        if (interceptors == null) {
            interceptors = new ArrayList<>();
        }
        interceptors.add(interceptor);
        return this;
    }


    public void get(CommandImpl command, Observer observer) {
        if (!NetworkUtils.isNetworkConnected()) {
            Observable.error(new NetworkErrorException("当前网络不可用")).subscribe(observer);
            return;
        }
        Observable observable = httpService.getData(command.getUrl(), command.getParams());
        if (command.getObject()) {
            requestForData(observable, observer, new ToBeanMap<>(command.getCls()));
        } else {
            requestForData(observable, observer, new ToListMap(command.getCls()));
        }
    }

    //add shibo.zheng start

   /* public void post(String url*//*, String token*//*, String json, Observer observer, Class cls) {
        if (!NetworkUtils.isNetworkConnected()) {
            Observable.error(new NetworkErrorException("当前网络不可用")).subscribe(observer);
            return;
        }
        RequestBody body = RequestBody.create(MEDIATYPEJSON, json);
        Observable observable = httpService.postBody(url, body);

        requestForData(observable, observer, new ToBeanMap<>(cls));

        *//*getInstance().postRequest(url, json, observer);*//*
    }*/

    public void post(String url, String json, Observer observer, Class cls) {
        if (!NetworkUtils.isNetworkConnected()) {
            Observable.error(new NetworkErrorException("当前网络不可用")).subscribe(observer);
            return;
        }
        RequestBody body = RequestBody.create(MEDIATYPEJSON, json);
        Observable observable = httpService.postBody(url, body);

        requestForData(observable, observer, new ToBeanMap<>(cls));

        /*getInstance().postRequest(url, json, observer);*/
    }

    //add shibo.zheng end

    public void post(CommandImpl command, Observer observer) {
        if (!NetworkUtils.isNetworkConnected()) {
            Observable.error(new NetworkErrorException("当前网络不可用")).subscribe(observer);
            return;
        }
        Observable observable = httpService.postData(command.getUrl(), command.getParams());
        if (command.getObject()) {
            requestForData(observable, observer, new ToBeanMap<>(command.getCls()));
        } else {
            requestForData(observable, observer, new ToListMap(command.getCls()));
        }
    }

    /**
     * 上传（不带文件的上传方法）
     * @param command
     * @param observer
     */
    public void postUrlForQuery(CommandImpl command, Observer observer) {
        if (!NetworkUtils.isNetworkConnected()) {
            Observable.error(new NetworkErrorException("当前网络不可用")).subscribe(observer);
            return;
        }
        Observable<String> observable = httpService.postUrlForQuery(command.getUrl(), command.getParams(), command.getFieldMap());
        if (command.getObject()) {
            requestForData(observable, observer, new ToBeanMap<>(command.getCls()));
        } else {
            requestForData(observable, observer, new ToListMap(command.getCls()));
        }
    }
//
//
//    public void getDataForJSONObject(CommandImpl command, Observer observer) {
//        if (!NetworkUtils.isNetworkConnected()) {
//            Observable.error(new NetworkErrorException("当前网络不可用")).subscribe(observer);
//            return;
//        }
//        Observable<JSONObject> observable = httpService.getDataForJSONObject(command.getUrl(), command.getParams());
//        requestForData(observable, observer, new ToBeanMap<>(JSONObject.class));
//    }

    /**
     * 上传文件
     *
     * @param
     * @param observer
     */
    public void uploadfile(CommandImpl command, Observer observer) {
        if (!NetworkUtils.isNetworkConnected()) {
            Observable.error(new NetworkErrorException("当前网络不可用")).subscribe(observer);
            return;
        }
        addInterceptor(new FileInterceptor());
        Observable<String> observable = httpService.uploadfile(command.getUrl(), command.getParams(), command.getBodys());
        requestForData(observable, observer, new ToBeanMap<>(command.getCls()));
    }

    public void getForboolean(CommandImpl command, Observer observer) {
        if (!NetworkUtils.isNetworkConnected()) {
            Observable.error(new NetworkErrorException("当前网络不可用")).subscribe(observer);
            return;
        }
        Observable<Boolean> observable = httpService.getDataForBoolean(command.getUrl(), command.getParams());
        requestForBoolean(observable, observer);
    }

    public void postForBoolean(CommandImpl command, Observer observer) {
        if (!NetworkUtils.isNetworkConnected()) {
            Observable.error(new NetworkErrorException("当前网络不可用")).subscribe(observer);
            return;
        }
        Observable<Boolean> observable = httpService.postDataForBoolean(command.getUrl(), command.getParams());
        requestForBoolean(observable, observer);
    }

    /**
     * @param observable
     * @param function
     * @return
     */
    private Observable requestForData(Observable observable, Function function) {
        return observable.map(function);

    }

    private void requestForData(Observable observable, Observer observer) {
        observable.compose(RxUtil.applySchedulersIO())
                .subscribe(observer);
    }

    private void requestForData(Observable observable, Observer observer, Function function) {
        observable.map(function)
                .compose(RxUtil.applySchedulersIO())
                .subscribe(observer);
    }

    private void requestForBoolean(Observable<Boolean> observable, Observer<Boolean> observer) {
        observable
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);
    }

    public <T> Observable<T> getObservable(CommandImpl command) {
        if (!NetworkUtils.isNetworkConnected()) {
            return Observable.error(new NetworkErrorException("当前网络不可用"));
        }
        Observable observable = httpService.getData(command.getUrl(), command.getParams());
        if (command.getObject()) {
            return requestForData(observable, new ToBeanMap<>(command.getCls()));
        } else {
            return requestForData(observable, new ToListMap(command.getCls()));
        }
    }

    public <T> Observable<T> postObservable(CommandImpl command) {
        if (!NetworkUtils.isNetworkConnected()) {
            return Observable.error(new NetworkErrorException("当前网络不可用"));
        }

        Observable observable = httpService.postData(command.getUrl(), command.getParams());
        if (command.getObject()) {
            return requestForData(observable, new ToBeanMap<>(command.getCls()));
        } else {
            return requestForData(observable, new ToListMap(command.getCls()));
        }
    }

    private static class SingleHolder {
        static HttpManager instance = new HttpManager();
    }

    private static class ToBeanMap<T> implements Function<String, T> {
        final Class<T> cls;

        ToBeanMap(Class<T> cls) {
            this.cls = cls;
        }

        @Override
        public T apply(@NonNull String json) throws Exception {
            if (TextUtils.isEmpty(json)) {
                return cls.newInstance();
            }

            if (cls.getName().equals(String.class.getName())) {
                return (T) json;
            }

            return JSON.parseObject(json, cls);
        }
    }

    private static class ToListMap implements Function<String, List> {

        private Class cls;

        ToListMap(Class cls) {
            this.cls = cls;
        }

        @Override
        public List apply(@NonNull String json) {
            if (TextUtils.isEmpty(json)) {
                return new ArrayList<>();
            }
            return JSON.parseArray(json, cls);
        }
    }
}
